//
//  BoardView.m
//  hexa
//
//  Created by kelly on 2014. 12. 31..
//  Copyright (c) 2014년 campmobile. All rights reserved.
//

#import "BoardView.h"

@implementation BoardView

- (void)setupViews:(id <FlowDelegate> )aDelegate
{
    delegate = aDelegate;
    removeIndexList = [NSMutableArray array];
    
    CGFloat widthSpacing = self.frame.size.width / BOARD_WIDTH_COUNT;
    for (NSInteger i = 0; i <= BOARD_WIDTH_COUNT; i++) {
        UIView *seperatorView = [[UIView alloc] initWithFrame:CGRectMake(widthSpacing * i, 0, SEPERATOR_WEIGHT, self.frame.size.height)];
        [seperatorView setBackgroundColor:[UIColor blackColor]];
        [self addSubview:seperatorView];
    }
    
    CGFloat heightSpacing = self.frame.size.height / BOARD_HEIGHT_COUNT;
    for (NSInteger i = 0; i <= BOARD_HEIGHT_COUNT; i++) {
        UIView *seperatorView = [[UIView alloc] initWithFrame:CGRectMake(0, heightSpacing * i, self.frame.size.width, SEPERATOR_WEIGHT)];
        [seperatorView setBackgroundColor:[UIColor blackColor]];
        [self addSubview:seperatorView];
    }
    
    for (NSInteger i = 0; i < BOARD_WIDTH_COUNT; i++) {
        for (NSInteger j = 0; j < BOARD_HEIGHT_COUNT; j++) {
            BoardCell *childBlockView = [[BoardCell alloc] initWithFrame:CGRectMake(widthSpacing * i + SEPERATOR_WEIGHT, heightSpacing * j + SEPERATOR_WEIGHT, widthSpacing - (SEPERATOR_WEIGHT * 2), heightSpacing - (SEPERATOR_WEIGHT * 2))];
            boardViewMap[i][j] = childBlockView;
            [self addSubview:childBlockView];
        }
    }
}

- (BOOL)place:(BlockType)aFirstBlockType second:(BlockType)aSecondBlockType third:(BlockType)aThirdBlockType
{
    NSInteger middleX = BOARD_WIDTH_COUNT / 2;
    if (boardTypeMap[middleX][2]) {
        return NO;
    }
    
    currentXIndex = middleX;
    currentYIndex = 0;
    
    firstBlockType = aFirstBlockType;
    secondBlockType = aSecondBlockType;
    thirdBlockType = aThirdBlockType;
    
    [self placeBlock:NO];
    
    return YES;
}

- (void)placeBlock:(BOOL)aNeedToRemove
{
    boardTypeMap[currentXIndex][currentYIndex] = firstBlockType;
    boardTypeMap[currentXIndex][currentYIndex + 1] = secondBlockType;
    boardTypeMap[currentXIndex][currentYIndex + 2] = thirdBlockType;
    
    [BlockTypeUtil draw:firstBlockType view:boardViewMap[currentXIndex][currentYIndex] i:currentXIndex j:currentYIndex];
    [BlockTypeUtil draw:secondBlockType view:boardViewMap[currentXIndex][currentYIndex + 1] i:currentXIndex j:currentYIndex + 1];
    [BlockTypeUtil draw:thirdBlockType view:boardViewMap[currentXIndex][currentYIndex + 2] i:currentXIndex j:currentYIndex + 2];
    NSLog(@"5 redraw = %ld, %ld, blockType = %ld", currentXIndex, currentYIndex, boardTypeMap[currentXIndex][currentYIndex]);
    
    if (aNeedToRemove) {
        needToRemove = YES;
    } else {
        needToRemove = NO;
    }
}

- (void)refresh
{
    for (NSInteger i = 0; i < BOARD_WIDTH_COUNT; i++) {
        for (NSInteger j = 0; j < BOARD_HEIGHT_COUNT; j++) {
            BoardCell *childBlockView = boardViewMap[i][j];
            BlockType blockType = boardTypeMap[i][j];
            if (childBlockView.tag != blockType) {
                NSLog(@"1 redraw = %ld, %ld, blockType = %ld", i, j, blockType);
                [BlockTypeUtil draw:blockType view:childBlockView i:i j:j];
            }
        }
    }
}

- (void)change
{
    boardTypeMap[currentXIndex][currentYIndex] = secondBlockType;
    boardTypeMap[currentXIndex][currentYIndex + 1] = thirdBlockType;
    boardTypeMap[currentXIndex][currentYIndex + 2] = firstBlockType;
    
    firstBlockType = boardTypeMap[currentXIndex][currentYIndex];
    secondBlockType = boardTypeMap[currentXIndex][currentYIndex + 1];
    thirdBlockType = boardTypeMap[currentXIndex][currentYIndex + 2];
    
    [BlockTypeUtil draw:firstBlockType view:boardViewMap[currentXIndex][currentYIndex] i:currentXIndex j:currentYIndex];
    [BlockTypeUtil draw:secondBlockType view:boardViewMap[currentXIndex][currentYIndex + 1] i:currentXIndex j:currentYIndex + 1];
    [BlockTypeUtil draw:thirdBlockType view:boardViewMap[currentXIndex][currentYIndex + 2] i:currentXIndex j:currentYIndex + 2];
    NSLog(@"6 redraw = %ld, %ld, blockType = %ld", currentXIndex, currentYIndex, boardTypeMap[currentXIndex][currentYIndex]);
}

- (void)flow
{
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(flows:) userInfo:nil repeats:YES];
}

- (void)flows:(NSTimer *)timer
{
    if (flowEnd) {
        if (needToRemove) {
            [self performSelectorOnMainThread:@selector(removeBlockList) withObject:nil waitUntilDone:YES];
        } else {
            [delegate end];
            flowEnd = NO;
        }
        return;
    }
    
    boardTypeMap[currentXIndex][currentYIndex] = BlockTypeNone;
    [BlockTypeUtil draw:boardTypeMap[currentXIndex][currentYIndex] view:boardViewMap[currentXIndex][currentYIndex] i:currentXIndex j:currentYIndex];
    NSLog(@"4 redraw = %ld, %ld, blockType = %ld", currentXIndex, currentYIndex, boardTypeMap[currentXIndex][currentYIndex]);
    
    currentYIndex++;
    if (currentYIndex + 2 == BOARD_HEIGHT_COUNT - 1) { // 화면의 맨 밑에 위치하는 경우
        needToRemove = boardTypeMap[currentXIndex][currentYIndex] && boardTypeMap[currentXIndex][currentYIndex + 1] && boardTypeMap[currentXIndex][currentYIndex + 2]; // 3개의 블럭이 모두 같은 경우
        [self placeBlock:needToRemove];
        if (!flowEnd) {
            flowEnd = YES;
        }
    } else if (boardTypeMap[currentXIndex][currentYIndex + 3] != BlockTypeNone) { // 다른 블럭이 밑에 있는 경우
        [self placeBlock:YES];
        if (!flowEnd) {
            flowEnd = YES;
        }
    } else { // 그냥 허공에 흘러가는 중
        [self placeBlock:NO];
    }
}

- (void)left
{
    if (currentXIndex - 1 < 0 || boardTypeMap[currentXIndex - 1][currentYIndex + 2] != BlockTypeNone) {
        return;
    }
    
    boardTypeMap[currentXIndex][currentYIndex] = BlockTypeNone;
    boardTypeMap[currentXIndex][currentYIndex + 1] = BlockTypeNone;
    boardTypeMap[currentXIndex][currentYIndex + 2] = BlockTypeNone;
    
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex] i:currentXIndex j:currentYIndex];
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex + 1] i:currentXIndex j:currentYIndex + 1];
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex + 2] i:currentXIndex j:currentYIndex + 2];
    
    boardTypeMap[currentXIndex - 1][currentYIndex] = firstBlockType;
    boardTypeMap[currentXIndex - 1][currentYIndex + 1] = secondBlockType;
    boardTypeMap[currentXIndex - 1][currentYIndex + 2] = thirdBlockType;
    
    [BlockTypeUtil draw:firstBlockType view:boardViewMap[currentXIndex - 1][currentYIndex] i:currentXIndex - 1 j:currentYIndex];
    [BlockTypeUtil draw:secondBlockType view:boardViewMap[currentXIndex - 1][currentYIndex + 1] i:currentXIndex - 1 j:currentYIndex + 1];
    [BlockTypeUtil draw:thirdBlockType view:boardViewMap[currentXIndex - 1][currentYIndex + 2] i:currentXIndex - 1 j:currentYIndex + 2];
    
    currentXIndex--;
    
    if (currentYIndex + 2 < BOARD_HEIGHT_COUNT - 1 && flowEnd) {
        NSLog(@"left just now");
        flowEnd = NO;
        needToRemove = NO;
        currentYIndex--;
        boardTypeMap[currentXIndex][currentYIndex] = firstBlockType;
        boardTypeMap[currentXIndex][currentYIndex + 1] = secondBlockType;
        boardTypeMap[currentXIndex][currentYIndex + 2] = thirdBlockType;
        [self flows:nil];
    }
}

- (void)right
{
    if (currentXIndex + 1 >= BOARD_WIDTH_COUNT || boardTypeMap[currentXIndex + 1][currentYIndex + 2] != BlockTypeNone) {
        return;
    }
    
    boardTypeMap[currentXIndex][currentYIndex] = BlockTypeNone;
    boardTypeMap[currentXIndex][currentYIndex + 1] = BlockTypeNone;
    boardTypeMap[currentXIndex][currentYIndex + 2] = BlockTypeNone;
    
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex] i:currentXIndex j:currentYIndex];
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex + 1] i:currentXIndex j:currentYIndex + 1];
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex + 2] i:currentXIndex j:currentYIndex + 2];
    
    boardTypeMap[currentXIndex + 1][currentYIndex] = firstBlockType;
    boardTypeMap[currentXIndex + 1][currentYIndex + 1] = secondBlockType;
    boardTypeMap[currentXIndex + 1][currentYIndex + 2] = thirdBlockType;
    
    [BlockTypeUtil draw:firstBlockType view:boardViewMap[currentXIndex + 1][currentYIndex] i:currentXIndex + 1 j:currentYIndex];
    [BlockTypeUtil draw:secondBlockType view:boardViewMap[currentXIndex + 1][currentYIndex + 1] i:currentXIndex + 1 j:currentYIndex + 1];
    [BlockTypeUtil draw:thirdBlockType view:boardViewMap[currentXIndex + 1][currentYIndex + 2] i:currentXIndex + 1 j:currentYIndex + 2];
    
    currentXIndex++;
    
    if (currentYIndex + 2 < BOARD_HEIGHT_COUNT - 1 && flowEnd) {
        NSLog(@"right just now");
        flowEnd = NO;
        needToRemove = NO;
        currentYIndex--;
        boardTypeMap[currentXIndex][currentYIndex] = firstBlockType;
        boardTypeMap[currentXIndex][currentYIndex + 1] = secondBlockType;
        boardTypeMap[currentXIndex][currentYIndex + 2] = thirdBlockType;
        [self flows:nil];
    }
}

- (void)down
{
    boardTypeMap[currentXIndex][currentYIndex] = BlockTypeNone;
    boardTypeMap[currentXIndex][currentYIndex + 1] = BlockTypeNone;
    boardTypeMap[currentXIndex][currentYIndex + 2] = BlockTypeNone;
    
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex] i:currentXIndex j:currentYIndex];
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex + 1] i:currentXIndex j:currentYIndex + 1];
    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[currentXIndex][currentYIndex + 2] i:currentXIndex j:currentYIndex + 2];
    
    NSInteger findIndex = BOARD_HEIGHT_COUNT - 1;
    while (findIndex >= 0) {
        if (boardTypeMap[currentXIndex][findIndex] == BlockTypeNone) {
            break;
        }
        findIndex--;
    }
    
    currentYIndex = findIndex - 2;
    
    [self placeBlock:YES];
    flowEnd = YES;
}

- (void)exploreRight:(NSInteger)xIndex yIndex:(NSInteger)yIndex firstCall:(BOOL)firstCall
{
    if ((firstCall && xIndex + 2 == BOARD_WIDTH_COUNT) || (!firstCall && xIndex + 1 == BOARD_WIDTH_COUNT)) {
        return;
    }
    
    if (firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex + 1][yIndex] && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex + 2][yIndex]) {
        [self addRemoveIndex:xIndex yIndex:yIndex];
        [self addRemoveIndex:xIndex + 1 yIndex:yIndex];
        [self addRemoveIndex:xIndex + 2 yIndex:yIndex];
        [self exploreRight:xIndex + 2 yIndex:yIndex firstCall:NO];
    } else if (!firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex + 1][yIndex]) {
        [self addRemoveIndex:xIndex + 1 yIndex:yIndex];
        [self exploreRight:xIndex + 1 yIndex:yIndex firstCall:NO];
    } else {
        return;
    }
}

- (void)explorerBottom:(NSInteger)xIndex yIndex:(NSInteger)yIndex firstCall:(BOOL)firstCall
{
    if ((firstCall && yIndex + 2 == BOARD_HEIGHT_COUNT) || (!firstCall && yIndex + 1 == BOARD_HEIGHT_COUNT)) {
        return;
    }
    
    if (firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex][yIndex + 1] && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex][yIndex + 2]) {
        [self addRemoveIndex:xIndex yIndex:yIndex];
        [self addRemoveIndex:xIndex yIndex:yIndex + 1];
        [self addRemoveIndex:xIndex yIndex:yIndex + 2];
        [self explorerBottom:xIndex yIndex:yIndex + 2 firstCall:NO];
    } else if (!firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex][yIndex + 1]) {
        [self addRemoveIndex:xIndex yIndex:yIndex + 1];
        [self explorerBottom:xIndex yIndex:yIndex + 1 firstCall:NO];
    } else {
        return;
    }
}

- (void)explorerBottomRight:(NSInteger)xIndex yIndex:(NSInteger)yIndex firstCall:(BOOL)firstCall
{
    if ((firstCall && (yIndex + 2 == BOARD_HEIGHT_COUNT || xIndex + 2 == BOARD_WIDTH_COUNT)) || (!firstCall && (yIndex + 1 == BOARD_HEIGHT_COUNT || xIndex + 1 == BOARD_WIDTH_COUNT))) {
        return;
    }
    
    if (firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex + 1][yIndex + 1] && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex + 2][yIndex + 2]) {
        [self addRemoveIndex:xIndex yIndex:yIndex];
        [self addRemoveIndex:xIndex + 1 yIndex:yIndex + 1];
        [self addRemoveIndex:xIndex + 2 yIndex:yIndex + 2];
        [self explorerBottomRight:xIndex + 2 yIndex:yIndex + 2 firstCall:NO];
    } else if (!firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex + 1][yIndex + 1]) {
        [self addRemoveIndex:xIndex + 1 yIndex:yIndex + 1];
        [self explorerBottomRight:xIndex + 1 yIndex:yIndex + 1 firstCall:NO];
    } else {
        return;
    }
}

- (void)explorerBottomLeft:(NSInteger)xIndex yIndex:(NSInteger)yIndex firstCall:(BOOL)firstCall
{
    if ((firstCall && (yIndex + 2 == BOARD_HEIGHT_COUNT || xIndex - 2 < 0)) || (!firstCall && (yIndex + 1 == BOARD_HEIGHT_COUNT || xIndex - 1 < 0))) {
        return;
    }
    
    if (firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex - 1][yIndex + 1] && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex - 2][yIndex + 2]) {
        [self addRemoveIndex:xIndex yIndex:yIndex];
        [self addRemoveIndex:xIndex - 1 yIndex:yIndex + 1];
        [self addRemoveIndex:xIndex - 2 yIndex:yIndex + 2];
        [self explorerBottomLeft:xIndex - 2 yIndex:yIndex + 2 firstCall:NO];
    } else if (!firstCall && boardTypeMap[xIndex][yIndex] == boardTypeMap[xIndex - 1][yIndex + 1]) {
        [self addRemoveIndex:xIndex - 1 yIndex:yIndex + 1];
        [self explorerBottomLeft:xIndex - 1 yIndex:yIndex + 1 firstCall:NO];
    } else {
        return;
    }
}

- (BOOL)findRemoveBlockList
{
    for (NSInteger i = 0; i < BOARD_WIDTH_COUNT; i++) {
        for (NSInteger j = 0; j < BOARD_HEIGHT_COUNT; j++) {
            if ([self contains:i yIndex:j]) {
                continue;
            }
            
            if (boardTypeMap[i][j] == BlockTypeNone) {
                continue;
            }
            
            [self exploreRight:i yIndex:j firstCall:YES];
            [self explorerBottomRight:i yIndex:j firstCall:YES];
            [self explorerBottom:i yIndex:j firstCall:YES];
            [self explorerBottomLeft:i yIndex:j firstCall:YES];
        }
    }
    
    return removeIndexList.count > 0;
}

- (void)removeBlockList
{
    if (![self findRemoveBlockList]) {
        needToRemove = NO;
        return;
    }
    
    // 구멍 뚫린 부분 제거
    for (NSInteger i = 0; i < BOARD_WIDTH_COUNT; i++) {
        NSMutableArray *blockList = [NSMutableArray array];
        for (NSInteger j = BOARD_HEIGHT_COUNT - 1; j >= 0; j--) {
            if (![self contains:i yIndex:j] && boardTypeMap[i][j] != BlockTypeNone) { // 구멍 뚤린 부분을 제거하고 맨 밑에부터 있는 것만 blockList에 채움
                [blockList addObject:@(boardTypeMap[i][j])];
            }
        }
        
        if (blockList.count > 0) {
            NSInteger index = 0;
            for (NSInteger j = BOARD_HEIGHT_COUNT - 1; j >= 0; j--) {
                if (blockList.count <= index) { // blockList를 넘어가는 인덱스(아직 블럭이 쌓이지 않은 부분)
                    boardTypeMap[i][j] = BlockTypeNone;
                    [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[i][j] i:i j:j];
                    NSLog(@"2 redraw = %ld, %ld, blockType = %ld", i, j, BlockTypeNone);
                } else { // blockList 안의 인덱스는 블럭으로 교체해줌
                    boardTypeMap[i][j] = [blockList[index] integerValue];
                    [BlockTypeUtil draw:boardTypeMap[i][j] view:boardViewMap[i][j] i:i j:j];
                    NSLog(@"3 redraw = %ld, %ld, blockType = %ld", i, j, boardTypeMap[i][j]);
                }
                index++;
            }
        } else {
            for (NSInteger j = BOARD_HEIGHT_COUNT - 1; j >= 0; j--) {
                boardTypeMap[i][j] = BlockTypeNone;
                [BlockTypeUtil draw:BlockTypeNone view:boardViewMap[i][j] i:i j:j];
                NSLog(@"7 redraw = %ld, %ld, blockType = %ld", i, j, BlockTypeNone);
            }
        }
    }
    
    [removeIndexList removeAllObjects];
    [self removeBlockList];
}

- (BOOL)contains:(NSInteger)xIndex yIndex:(NSInteger)yIndex
{
    for (NSDictionary *map in removeIndexList) {
        NSArray *keys = [map allKeys];
        NSInteger x = [keys[0] integerValue];
        NSInteger y = [[map objectForKey:keys[0]] integerValue];
        if (x == xIndex && y == yIndex) {
            return YES;
        }
    }
    
    return NO;
}

- (void)addRemoveIndex:(NSInteger)xIndex yIndex:(NSInteger)yIndex
{
    if ([self contains:xIndex yIndex:yIndex]) {
        return;
    }
    
    [removeIndexList addObject:@{ @(xIndex):@(yIndex) }];
}

@end
